/*

Copyright (C) 2000 - 2008 Christian Kreibich <christian@whoop.org>.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to
deal in the Software without restriction, including without limitation the
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
sell copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies of the Software and its documentation and acknowledgment shall be
given in the documentation and software packages that this Software was
used.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

*/
#ifdef HAVE_CONFIG_H
#  include <config.h>
#endif

#include <sys/types.h>
#include <time.h>
#include <stdio.h>
#include <errno.h>

#include <nd_filter_gui.h>
#include <nd_trace.h>
#include <nd_trace_registry.h>
#include <interface.h>
#include <callbacks.h>


static GtkWidget      *filter_dialog = NULL;
static GtkWidget      *trace_filter_dialog = NULL;

static void trace_filter_dialog_update(void);

static void
filter_changed(LND_Filter *filter)
{
  D_ENTER;
  nd_filter_dialog_update();
  D_RETURN;
  TOUCH(filter);
}


void             
nd_filter_dialog_init(void)
{
  LND_FilterRegObserver *ob;

  if (! (ob = libnd_filter_registry_observer_new()))
    {
      D(("Out of memory.\n"));
      return;
    }

  ob->filter_added   = filter_changed;
  ob->filter_deleted = filter_changed;
  libnd_filter_registry_add_observer(ob);
}


static void 
filter_dialog_list_cb(LND_Filter *filter, void *user_data)
{
  GtkWidget *filter_list, *list_item;

  ND_GTK_GET(filter_list, filter_dialog, "filters_list");

  /* Append the filter to the list widget in the upper part of the dialog */
  list_item = gtk_list_item_new_with_label(filter->name);
  gtk_object_set_data(GTK_OBJECT(list_item), "filter", filter);
  gtk_container_add(GTK_CONTAINER(filter_list), list_item);
  gtk_widget_show(list_item);

  return;
  TOUCH(user_data);
}

void             
nd_filter_dialog_update(void)
{
  GtkWidget *w;

  /* First remove old elements from both list and vbox */
  ND_GTK_GET(w, filter_dialog, "filters_list");
  gtk_list_clear_items(GTK_LIST(w), 0, -1);

  /* And then add new widgets */
  libnd_filter_registry_foreach(filter_dialog_list_cb, filter_dialog);
  trace_filter_dialog_update();
}


static void
filter_dialog_optionmenu_cb(LND_FilterFactory *factory, void *user_data)
{
  GtkWidget *menu_item;

  menu_item = gtk_menu_item_new_with_label(factory->name);
  gtk_object_set_data(GTK_OBJECT(menu_item), "factory", factory);
  gtk_widget_show(menu_item);
  gtk_menu_append(GTK_MENU(user_data), menu_item);
}


void      
nd_filter_dialog_show(void)
{
  LND_Trace         *trace;
  GtkWidget         *w, *option_menu;

  trace = nd_trace_registry_get_current();

  if (!filter_dialog)
    filter_dialog = create_filter_dialog();
  
  /* Set up the filter selection combobox */
  ND_GTK_GET(w, filter_dialog, "filter_optionmenu");
  gtk_option_menu_remove_menu(GTK_OPTION_MENU(w));
  option_menu = gtk_menu_new ();
   
  libnd_filter_factory_foreach(filter_dialog_optionmenu_cb, option_menu);
  gtk_option_menu_set_menu(GTK_OPTION_MENU(w), option_menu);

  /* Set up the list of existing filters (top part of dialog) */
  nd_filter_dialog_update();

  gtk_widget_show(filter_dialog);
}


void      
nd_filter_dialog_hide(void)
{
  if (filter_dialog)
    gtk_widget_hide(filter_dialog);
}


void             
nd_filter_dialog_create(void)
{
  GtkWidget *w, *menu_item;
  LND_FilterFactory *factory;

  if (!filter_dialog)
    return;

  ND_GTK_GET(w, filter_dialog, "filter_optionmenu");
  menu_item = GTK_OPTION_MENU(w)->menu_item;
  ND_GTK_GET(factory, menu_item, "factory");
  
  if (!factory)
    return;

  /* Create new filter according to factory */
  factory->create_func();
}


static LND_Filter *
filter_dialog_get_selected_filter(void)
{
  GtkList *list;
  LND_Filter *filter;

  ND_GTK_GET(list, filter_dialog, "filters_list");
  
  if (! list->selection)
    return NULL;

  ND_GTK_GET(filter, list->selection->data, "filter");
  return filter;
}


void             
nd_filter_dialog_delete(void)
{
  LND_Filter *filter;

  D_ENTER;

  if (! (filter = filter_dialog_get_selected_filter()))
    D_RETURN;

  D(("Removing filter %s from all traces\n", filter->name));
  nd_trace_registry_foreach((ND_TraceFunc) libnd_trace_remove_filter, filter);
  libnd_filter_registry_del(filter);
  libnd_filter_free(filter);
}


void             
nd_filter_dialog_modify(void)
{
  LND_Filter *filter;

  if (! (filter = filter_dialog_get_selected_filter()))
    return;
  
  if (! filter->factory)
    {
      nd_dialog_message(_("Filter Problem"),
			_("This filter cannot be modified."),
			TRUE);
      return;
    }

  filter->factory->modify_func(filter);
  nd_filter_dialog_update();
  trace_filter_dialog_update();
}



static void             
trace_filter_add_checkbox_cb(LND_Filter *filter, GtkWidget *dialog)
{
  GtkWidget *filter_vbox, *checkbutton;

  ND_GTK_GET(filter_vbox, trace_filter_dialog, "filters_vbox");

  /* Add a checkbutton for the filter. We hook the real filter
   * structure into the checkboxes so that the filters are easily
   * available when we update a trace's settings.
   */
  checkbutton = gtk_check_button_new_with_label(filter->name);
  gtk_widget_ref(checkbutton);
  gtk_object_set_data_full(GTK_OBJECT(dialog), filter->name, checkbutton,
                            (GtkDestroyNotify) gtk_widget_unref);
  gtk_object_set_data(GTK_OBJECT(checkbutton), "filter", filter);
  gtk_widget_show(checkbutton);
  gtk_box_pack_start(GTK_BOX(filter_vbox), checkbutton, FALSE, FALSE, 0);
}


static void
trace_filter_dialog_update(void)
{
  GList *l, *kids;
  GtkWidget *w;
  GtkToggleButton *button;
  LND_Filter *filter;
  LND_Trace *trace;

  return_if_no_current_trace(trace);

  if (!trace_filter_dialog)
    trace_filter_dialog = create_trace_filter_dialog();

  /* Create a checkbox for every currently existing filter: */
  ND_GTK_GET(w, trace_filter_dialog, "filters_vbox");
  kids = gtk_container_children(GTK_CONTAINER(w));
  for (l = kids; l; l = g_list_next(l))
    gtk_container_remove(GTK_CONTAINER(w), l->data);
  g_list_free(kids);

  libnd_filter_registry_foreach((LND_FilterRegFunc) trace_filter_add_checkbox_cb,
				trace_filter_dialog);  


  /* Sync dialog to currently selected trace: */
  
  ND_GTK_GET(w, trace_filter_dialog, "filter_mode_and_radiobutton");
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w),
			       (trace->filter_mode == LND_FILTER_MODE_AND));
  ND_GTK_GET(w, trace_filter_dialog, "filter_mode_or_radiobutton");
  gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(w),
			       (trace->filter_mode == LND_FILTER_MODE_OR));


  ND_GTK_GET(w, trace_filter_dialog, "filters_vbox");
  kids = gtk_container_children(GTK_CONTAINER(w));

  for (l = kids; l; l = g_list_next(l))
    {
      button = GTK_TOGGLE_BUTTON(l->data);
      ND_GTK_GET(filter, button, "filter");

      gtk_toggle_button_set_active(button, libnd_trace_has_filter(trace, filter));
    }

  g_list_free(kids);  
}


void             
nd_trace_filter_dialog_show(void)
{
  LND_Trace *trace;

  return_if_no_current_trace(trace);

  if (!trace_filter_dialog)
    trace_filter_dialog = create_trace_filter_dialog();
  
  trace_filter_dialog_update();
  gtk_widget_show(trace_filter_dialog);
}


void             
nd_trace_filter_dialog_apply_filters(void)
{
  GList *l, *kids;
  GtkWidget *w;
  GtkToggleButton *button;
  LND_Filter *filter;  
  LND_Trace *trace;

  return_if_no_current_trace(trace);

  /* Update the filters list in the trace */
  ND_GTK_GET(w, trace_filter_dialog, "filters_vbox");
  kids = gtk_container_children(GTK_CONTAINER(w));

  /* Clear old filters out -- this will potentially
   * sync the trace to the current output basefile
   * if the user has performed operations in apply-to-all
   * mode with the current filter setting.
   */
  libnd_trace_clear_filters(trace);

  for (l = kids; l; l = g_list_next(l))
    {
      button = GTK_TOGGLE_BUTTON(l->data);
      ND_GTK_GET(filter, button, "filter");

      if (gtk_toggle_button_get_active(button))
	{
	  D(("Trace %s now uses filter %s\n",
	     trace->filename, filter->name));
	  libnd_trace_add_filter(trace, filter);
	}
    }

  g_list_free(kids);  

  /* Now update the filter mode in the trace -- AND or OR */
  ND_GTK_GET(w, trace_filter_dialog, "filter_mode_and_radiobutton");

  if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(w)))
    trace->filter_mode = LND_FILTER_MODE_AND;
  else
    trace->filter_mode = LND_FILTER_MODE_OR;
  
  D(("Trace filter mode now: %i\n", trace->filter_mode));

  if (libnd_trace_apply_filters(trace) == 0)
    {
      nd_dialog_message(_("Filter Warning"),
			_("The filters were not applied to any packets.\n"
			  "Are you sure you are using the correct iteration mode?\n"),
			TRUE);
    }
}


void             
nd_trace_filter_dialog_cancel(void)
{
  if (trace_filter_dialog)
    gtk_widget_hide(trace_filter_dialog);
}

